/*
 * Copyright 2002-2006 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package net.jwtools.tmt.ui;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.sql.SQLException;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;

import net.jwtools.tmt.domain.IVisit;
import net.jwtools.tmt.domain.Territory;
import net.jwtools.tmt.service.IVisitService;
import net.jwtools.tmt.service.TerritoryService;
import net.jwtools.tmt.service.VisitService;
import net.jwtools.tmt.ui.table.VisitsTable;

import org.jdesktop.swingx.JXLabel;
import org.jdesktop.swingx.JXPanel;
import org.springframework.binding.value.ValueModel;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.richclient.application.PageComponentContext;
import org.springframework.richclient.application.event.LifecycleApplicationEvent;
import org.springframework.richclient.application.support.AbstractView;
import org.springframework.richclient.command.ActionCommand;
import org.springframework.richclient.command.CommandGroup;
import org.springframework.richclient.command.GuardedActionCommandExecutor;
import org.springframework.richclient.command.support.AbstractActionCommandExecutor;
import org.springframework.richclient.command.support.GlobalCommandIds;
import org.springframework.richclient.dialog.ConfirmationDialog;
import org.springframework.richclient.factory.DefaultComponentFactory;
import org.springframework.richclient.list.ListSelectionValueModelAdapter;
import org.springframework.richclient.list.ListSingleSelectionGuard;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.FilterList;

/**
 * This class provides the main view of the contacts. It provides a table
 * showing the contact objects and a quick filter field to narrow down the list
 * of visible contacts. Several commands are tied to the selection of the
 * contacts table
 * <p>
 * By implementing special tag interfaces, this component will be automatically
 * wired in to certain events of interest.
 * <ul>
 * <li><b>ApplicationListener</b> - This component will be automatically
 * registered as a listener for application events.</li>
 * </ul>
 * 
 * @author Larry Streepy
 */
public class VisitsView extends AbstractView implements ApplicationListener {

	/** The object table holding our contacts. */
	private VisitsTable visitsTable;

	/**
	 * Handler for the "New Contact" action.
	 */
	private GuardedActionCommandExecutor checkoutExecutor = new CheckoutExecutor();

	/**
	 * Handler for the "Properties" action.
	 */
	private GuardedActionCommandExecutor detailsExecutor = new DetailsExecutor();

	/**
	 * Handler for the "Properties" action.
	 */
	private GuardedActionCommandExecutor propertiesExecutor = new PropertiesExecutor();

	/**
	 * Handler for the "Delete" action.
	 */
	private GuardedActionCommandExecutor deleteExecutor = new DeleteExecutor();

	/**
	 * The text field allowing the user to filter the contents of the contact
	 * table.
	 */
	private TerritoryMapViewer imagePanel;

	private JXPanel detailPanel;

	private JXLabel detailInfo;
	
	private TerritoryTypeMatcherEditor tte;

	private JPanel filterPanel;

	/**
	 * Default constructor.
	 */
	public VisitsView() {
	}

	/**
	 * @return the visitService
	 */
	protected IVisitService getVisitService() {
		return (IVisitService) getApplicationServices().getService(
				VisitService.class);
	}

	protected void initListeners() {

	}

	/**
	 * Create the control for this view. This method is called by the platform
	 * in order to obtain the control to add to the surrounding window and page.
	 * 
	 * @return component holding this view
	 */
	protected JComponent createControl() {
		DefaultComponentFactory cf = new DefaultComponentFactory();

		JSplitPane control = new JSplitPane();
		visitsTable = new VisitsTable();
		visitsTable = new VisitsTableFactory().createVisitsTable();
		filterPanel = new JPanel(new BorderLayout());
		filterPanel.add(tte.getComponent(), BorderLayout.CENTER);
		filterPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

		control.setLeftComponent(filterPanel);

		JPanel resultTable = new JPanel(new BorderLayout());
		JScrollPane sp = getComponentFactory().createScrollPane(
				visitsTable.getControl());
		resultTable.add(filterPanel, BorderLayout.NORTH);
		resultTable.add(sp, BorderLayout.CENTER);
		sp.setPreferredSize(new java.awt.Dimension(608, 181));

		ActionCommand ac = (ActionCommand) getWindowCommandManager()
				.getCommand("checkoutCommand", ActionCommand.class);

		JPanel buttonPanel = new JPanel();
		FlowLayout buttonPanelLayout = new FlowLayout();
		buttonPanelLayout.setAlignment(FlowLayout.RIGHT);
		buttonPanel.setLayout(buttonPanelLayout);
		JButton checkOutButton = (JButton) ac.createButton();
		FlowLayout checkOutButtonLayout = new FlowLayout();
		checkOutButtonLayout.setAlignment(FlowLayout.RIGHT);
		checkOutButton.setLayout(checkOutButtonLayout);
		// checkOutButton.setText("Check out");
		buttonPanel.add(checkOutButton); // TargetableActionCommand ac = new
		// TargetableActionCommand();
		// ac.attach(newVisitButton);
		// ac.setCommandExecutor(newVisitExecutor);
		// buttonPanel.add(newVisitButton);
		resultTable.add(buttonPanel, BorderLayout.SOUTH);
		imagePanel = new TerritoryMapViewer();
		imagePanel.setPreferredSize(new java.awt.Dimension(461, 336));
		detailPanel = new JXPanel();
		detailPanel.setPreferredSize(new java.awt.Dimension(543, 288));
		detailPanel.setLayout(new BorderLayout());
		detailPanel.add(imagePanel, BorderLayout.WEST);
		detailInfo = new JXLabel();
		detailPanel.add(detailInfo, BorderLayout.CENTER);
		JSplitPane resultView = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
		resultView.setLeftComponent(resultTable);
		resultTable.setPreferredSize(new java.awt.Dimension(608, 229));
		resultView.setRightComponent(detailPanel);
		control.setRightComponent(resultView);
		resultView.setPreferredSize(new java.awt.Dimension(610, 543));
		initListeners();
		return control;
	}

	/**
	 * Register the local command executors to be associated with named
	 * commands. This is called by the platform prior to making the view
	 * visible.
	 */
	protected void registerLocalCommandExecutors(PageComponentContext context) {
		context.register("checkoutCommand", checkoutExecutor);
		context.register(GlobalCommandIds.PROPERTIES, propertiesExecutor);
		context.register(GlobalCommandIds.DELETE, deleteExecutor);
	}

	/**
	 * Prepare the table holding all the Contact objects. This table provides
	 * pretty much all the functional operations within this view. Prior to
	 * calling this method the {@link #setContactTable(VisitsTable)} will have
	 * already been called as part of the context bean creation.
	 */
	private class VisitsTableFactory {
		public VisitsTable createVisitsTable() {
			VisitsTable visitsTable = new VisitsTable();
			
			// Get the table instance from our factory
			// Make a double click invoke the properties dialog and plugin the
			// context menu
			visitsTable.setDoubleClickHandler(propertiesExecutor);
			visitsTable.setClickHandler(detailsExecutor);
			// Construct and install our filtering list. This filter will allow
			// the user
			// to simply type data into the txtFilter (JTextField). With the
			// configuration
			// setup below, the text entered by the user will be matched against
			// the values
			// in the lastName and address.address1 properties of the contacts
			// in the table.
			// The GlazedLists filtered lists is used to accomplish this.
			EventList baseList = visitsTable.getBaseEventList();
			tte = new TerritoryTypeMatcherEditor(baseList);

			// TextFilterator filterator = GlazedLists
			// .textFilterator(new String[] { "checkoutBy.displayName",
			// "territory.number" });

			FilterList filterList = new FilterList(baseList, tte);
			// Install the fully constructed (layered) list into the table

			visitsTable.setFinalEventList(filterList);

			// Install the popup menu
			CommandGroup popup = new CommandGroup();
			popup.add((ActionCommand) getWindowCommandManager().getCommand(
					"deleteCommand", ActionCommand.class));
			popup.addSeparator();
			popup.add((ActionCommand) getWindowCommandManager().getCommand(
					"propertiesCommand", ActionCommand.class));
			visitsTable.setPopupCommandGroup(popup);
			// visitsTable.
			// Register to get notified when the filtered list changes
			visitsTable.setStatusBar(getStatusBar());
			// Ensure our commands are only active when something is selected.
			// These guard objects operate by inspecting a list selection model
			// (held within a ValueModel) and then either enabling or disabling
			// the
			// guarded object (our executors) based on the configured criteria.
			// This configuration greatly simplifies the interaction between
			// commands
			// that require a selection on which to operate.
			ValueModel selectionHolder = new ListSelectionValueModelAdapter(
					visitsTable.getSelectionModel());
			new ListSingleSelectionGuard(selectionHolder, deleteExecutor);
			new ListSingleSelectionGuard(selectionHolder, propertiesExecutor);
			new ListSingleSelectionGuard(selectionHolder, detailsExecutor);
			new ListSingleSelectionGuard(selectionHolder, checkoutExecutor);

			return visitsTable;
		}
	}

	/**
	 * Private inner class to create a new contact.
	 */
	private class CheckoutExecutor extends AbstractActionCommandExecutor {
		public void execute() {
			final IVisit visit = visitsTable.getSelectedVisit();
			new TerritoryCheckoutDialog(visit, getVisitService()).showDialog();
		}
	}

	private class DetailsExecutor extends AbstractActionCommandExecutor {
		public void execute() {
			final IVisit visit = visitsTable.getSelectedVisit();
			BufferedImage image = null;
			Territory territory = null;
			String info = "";
			if (visit != null) {
				TerritoryService ts = (TerritoryService) getApplicationServices()
						.getService(TerritoryService.class);
				territory = ts.getTerritoryById(visit.getTerritory().getId());
				info = territory.getNumber() + territory.getDescription();
				try {
					image = ImageIO.read(territory.getMap().getBinaryStream());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			imagePanel.setOverview(image);
			detailInfo.setText(info);
		}
	}

	/**
	 * Private inner class to handle the properties form display.
	 */
	private class PropertiesExecutor extends AbstractActionCommandExecutor {
		public void execute() {
			// We know exactly one contact will be selected at this time because
			// of the guards put in place in prepareTable.
			// new ContactPropertiesDialog(visitsTable.getSelectedVisit(),
			// getContactDataStore()).showDialog();
			new TerritoryCheckoutDialog(visitsTable.getSelectedVisit(),
					getVisitService()).showDialog();
		}
	}

	/**
	 * Private class to handle the delete command. Note that due to the
	 * configuration above, this executor is only enabled when exactly one
	 * contact is selected in the table. Thus, we don't have to protect against
	 * being executed with an incorrect state.
	 */
	private class DeleteExecutor extends AbstractActionCommandExecutor {
		public void execute() {
			// We know exactly one contact will be selected at this time because
			// of the guards put in place in prepareTable.
			final IVisit visit = visitsTable.getSelectedVisit();
			// Query the user to be sure they want to do this
			String title = getMessage("contact.confirmDelete.title");
			String message = getMessage("contact.confirmDelete.message");
			ConfirmationDialog dlg = new ConfirmationDialog(title, message) {
				protected void onConfirm() {
					// Delete the object from the persistent store.
					// getContactDataStore().delete(visit);
					// And notify the rest of the application of the change
					getApplicationContext().publishEvent(
							new LifecycleApplicationEvent(
									LifecycleApplicationEvent.DELETED, visit));
				}
			};
			dlg.showDialog();
		}
	}

	/**
	 * Handle an application event. This will notify us of object adds, deletes,
	 * and modifications. Forward to our object table for handling.
	 * 
	 * @param e
	 *            event to process
	 */
	public void onApplicationEvent(ApplicationEvent e) {
		visitsTable.onApplicationEvent(e);
	}
}